#include <drv/hpet.h>
#include <drv/acpi.h>

#include <arch/io.h>

#define HPET_CAPABILITY 0x00
#define HPET_CONFIG 0x10
#define HPET_COUNTER 0xF0

typedef struct __packed {
	acpi_table_header_t header;
	uint8_t hardware_revision;
	uint8_t comparator_count : 5;
	uint8_t counter_size : 1;
	uint8_t reserved : 1;
	uint8_t legacy_replacement : 1;
	uint16_t pci_vendor_id;
	acpi_io_address_t hpet_address;
	uint8_t hpet_number;
	uint16_t minimum_tick;
	uint8_t page_protection;
} acpi_hpet_t;

static uintptr_t hpet_base = 0;
static uint32_t tick_period = 0;	// in nanoseconds

void hpet_init() {
	acpi_hpet_t *table = (acpi_hpet_t *)acpi_get_table("HPET");

	if(!table) {
		__asm__ volatile("hlt");
	}

	hpet_base = table->hpet_address.address;

	tick_period = mmio_read64(hpet_base, HPET_CAPABILITY) >> 32;
	tick_period /= 1000000;

	mmio_write64(hpet_base, HPET_CONFIG, BIT(0));
}

uint64_t hpet_read_counter() {
	return mmio_read64(hpet_base, HPET_COUNTER);
}

uint64_t hpet_get_timestamp() {
	return hpet_read_counter() * tick_period;
}

void hpet_sleep(uint64_t ns) {
	uint64_t target = hpet_get_timestamp() + ns;
	while(hpet_get_timestamp() < target);
}